using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEngine.UI;
using System.IO;
using System.Text;

public class AStar : MonoBehaviour
{
    public struct Point
    {
        public int x, y;
        public Point(int _x, int _y) {
            x = _x;
            y = _y;
        }
    }

    public class Node
    {
        public Point point;
        public Node parent;
        public double f, g, h;
    }

    public Tilemap map;
    public Button btn;
    private Dictionary<Point, Node> open;
    private HashSet<Point> closed;
    private Vector3Int start;
    private Vector3Int end;
    private string filepath;

    // Start is called before the first frame update
    void Start()
    {
        filepath = @"C:\Users\caomb\Desktop\实训\路径.txt";
        start = new Vector3Int(-51, -44, 0);
        end = new Vector3Int(-47, -41, 0);
        btn.onClick.AddListener(foo);
    }

    void foo()
    {
        open = new Dictionary<Point, Node>();
        closed = new HashSet<Point>();
        Point des = new Point(end.x, end.y);
        Node init = new Node();
        init.point = new Point(start.x, start.y);
        init.parent = null;
        init.g = 0;
        init.h = Calculate(init.point, des);
        init.f = init.g + init.h;
        open.Add(init.point, init);
        while(open.Count > 0) {
            Node min = GetMin(open);
            Point p = min.point;
            if(p.x == end.x && p.y == end.y) {
                Display(min);
                Debug.Log("Reachable");
                return;
            }
            open.Remove(min.point);
            closed.Add(min.point);
            for(int i = -1; i <= 1; i++) {
                for(int j = -1; j <= 1; j++) {
                    if(i == 0 && j == 0) {
                        continue;
                    }
                    if(i == 0 || j == 0) {
                        AddToOpen(new Point(p.x+i, p.y+j), min, 0);
                    } else {
                        AddToOpen(new Point(p.x+i, p.y+j), min, 1);
                    }
                }
            }
        }
        Debug.Log("Unreachable");
    }

    double Calculate(Point p1, Point p2)
    {
        int x1 = p1.x, x2 = p2.x, y1 = p1.y, y2 = p2.y;
        int offsetX = Math.Abs(x1 - x2), offsetY = Math.Abs(y1 - y2);
        int square = offsetX * offsetX + offsetY * offsetY;
        double res = Math.Pow(square, 0.5);
        return res;
    }

    Node GetMin(Dictionary<Point, Node> open)
    {
        Node min = null;
        foreach(var node in open) {
            if(min == null || node.Value.f < min.f) {
                min = node.Value;
            }
        }
        return min;
    }

    void Display(Node node)
    {
        using(StreamWriter sw = new StreamWriter(filepath, false)) {
            while(node != null) {
                sw.WriteLine("(" + node.point.x + ", " + node.point.y + ")");
                node = node.parent;
            }
        }
    }

    void AddToOpen(Point p, Node parent, int flag)
    {
        if(map.HasTile(new Vector3Int(p.x, p.y, 0)) && !closed.Contains(p)) {
            Node n = new Node();
            n.point = p;
            n.parent = parent;
            if(flag == 0) {
                n.g = parent.g + 1;
            } else {
                n.g = parent.g + 1.4;
            }
            n.h = Calculate(p, new Point(end.x, end.y));
            n.f = n.g + n.h;
            if(open.ContainsKey(p)) {
                if(n.f < open[p].f) {
                    open[p] = n;
                }
            } else {
                open.Add(p, n);
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
